package com.karina.visualization.chart.impl;

import gwt.g2d.client.graphics.Color;
import gwt.g2d.client.graphics.ColorStop;
import gwt.g2d.client.graphics.Composition;
import gwt.g2d.client.graphics.DirectShapeRenderer;
import gwt.g2d.client.graphics.Gradient;
import gwt.g2d.client.graphics.LineCap;
import gwt.g2d.client.graphics.LinearGradient;
import gwt.g2d.client.graphics.RadialGradient;
import gwt.g2d.client.graphics.Surface;
import gwt.g2d.client.graphics.TextAlign;
import gwt.g2d.client.graphics.TextBaseline;
import gwt.g2d.client.math.Vector2;

import java.util.List;

import com.karina.visualization.chart.client.Gradient.Entry;
import com.karina.visualization.chart.client.Graphics;

public class CanvasGraphics implements Graphics {

	private final DirectShapeRenderer canvas;

	private final Surface surface;

	// private final GWTCanvas canvas;

	private final double origionalX = 0;
	private final double origiaonlY = 0;

	private double width;
	private double height;

	private class Point {
		double x;
		double y;

		public Point(double x, double y) {
			this.x = x;
			this.y = y;
		}

	}

	public CanvasGraphics(Surface s) {
		surface = s;
		canvas = new DirectShapeRenderer(s);
	}

	@Override
	public Graphics beginPath() {
		canvas.beginPath();
		return this;
	}

	@Override
	public Graphics clear() {
		surface.clear();
		return this;
	}

	@Override
	public Graphics endPath() {
		canvas.closePath();
		return this;
	}

	private Point transferXY(Point pt) {
		Point newPt = new Point(pt.x, pt.y);
		newPt.x = pt.x + origionalX;
		newPt.y = pt.y + origiaonlY;
		return newPt;
	}

	@Override
	public Graphics arc(double x, double y, double radius, double startAngle,
			double endAngle, boolean antiClockwise) {
		Point pt = transferXY(new Point(x, y));
		canvas.drawArc(pt.x, pt.y, radius, startAngle, endAngle, antiClockwise);
		return this;
	}

	public LinearGradient createLinearGradient(double x0, double y0, double x1,
			double y1) {
		Point pt = transferXY(new Point(x0, y0));
		Point pt1 = transferXY(new Point(x1, y1));
		return new LinearGradient(pt.x, pt.y, pt1.x, pt1.y);
	}

	public RadialGradient createRadialGradient(double x0, double y0, double r0,
			double x1, double y1, double r1) {
		Point pt = transferXY(new Point(x0, y0));
		Point pt1 = transferXY(new Point(x1, y1));
		return new RadialGradient(pt.x, pt.y, r0, pt1.x, pt1.y, r1);
	}

	@Override
	public Graphics fill() {
		canvas.fill();
		return this;
	}

	@Override
	public int getCoordHeight() {
		// return surface.getHeight();
		return (int) height;
	}

	@Override
	public int getCoordWidth() {
		// return surface.getWidth();
		return (int) width;
	}

	@Override
	public double getGlobalAlpha() {
		return surface.getGlobalAlpha();
	}

	@Override
	public Graphics lineTo(double x, double y) {
		Point pt = transferXY(new Point(x, y));
		canvas.drawLineTo(pt.x, pt.y);
		return this;
	}

	@Override
	public Graphics moveTo(double x, double y) {
		Point pt = transferXY(new Point(x, y));
		canvas.moveTo(pt.x, pt.y);
		return this;
	}

	@Override
	public Graphics rect(double startX, double startY, double width,
			double height) {
		Point pt = transferXY(new Point(startX, startY));
		canvas.drawRect(pt.x, pt.y, width, height);
		return this;
	}

	@Override
	public Graphics restoreContext() {
		surface.restore();
		return this;
	}

	@Override
	public Graphics saveContext() {
		surface.save();
		return this;
	}

	@Override
	public Graphics setCoordHeight(int height) {
		surface.setHeight(height);
		this.height = height;
		return this;
	}

	@Override
	public Graphics setCoordWidth(int width) {
		surface.setWidth(width);
		this.width = width;
		return this;
	}

	@Override
	public Graphics setGlobalAlpha(double alpha) {
		surface.setGlobalAlpha(alpha);
		return this;
	}

	@Override
	public Graphics setLineWidth(double width) {
		surface.setLineWidth(width);
		return this;
	}

	@Override
	public Graphics stroke() {
		canvas.stroke();
		return this;
	}

	@Override
	public Graphics translate(double x, double y) {
		surface.translate(x, y);
		return this;
	}

	@Override
	public Graphics drawCircle(double x, double y, int radius) {
		canvas.drawArc(x, y, radius, 0, 2 * Math.PI, true);
		return this;
	}

	@Override
	public Graphics drawText(double x, double y, String text, String alignType,
			String baseLine) {
		if (alignType == "center") {
			surface.setTextAlign(TextAlign.CENTER);
		} else if (alignType == "left") {
			surface.setTextAlign(TextAlign.LEFT);
		} else {
			surface.setTextAlign(TextAlign.RIGHT);
		}

		if (baseLine == "top") {
			surface.setTextBaseline(TextBaseline.TOP);
		} else if (baseLine == "bottom") {
			surface.setTextBaseline(TextBaseline.BOTTOM);
		} else if (baseLine == "middle") {
			surface.setTextBaseline(TextBaseline.MIDDLE);
		}

		surface.fillText(text, x, y);

		return this;
	}

	@Override
	public void setLineColor(int r, int g, int b, double alpha) {
		surface.setStrokeStyle(new Color(r, g, b, alpha));
	}

	@Override
	public Graphics setJointCap(String type) {
		if (type == "round") {
			surface.setLineCap(LineCap.ROUND);
		} else if (type == "butt") {
			surface.setLineCap(LineCap.BUTT);
		} else {
			surface.setLineCap(LineCap.SQUARE);
		}

		return this;
	}

	@Override
	public Graphics fillStyle(int red, int green, int blue, double alpha) {
		surface.setFillStyle(new Color(red, green, blue, alpha));
		return this;
	}

	@Override
	public Graphics fillStyle(double x0, double y0, double x1, double y1,
			com.karina.visualization.chart.client.Gradient gradient) {
		Gradient g = new LinearGradient(x0, y0, x1, y1);

		List<Entry> entries = gradient.getColroPattern();

		for (Entry e : entries) {
			g.addColorStop(new ColorStop(e.radius, new Color(e.color.r,
					e.color.g, e.color.b)));
		}

		surface.setFillStyle(g);

		return this;
	}

	@Override
	public Graphics strokeWidth(int lineWidth) {
		surface.setLineWidth(lineWidth);
		return this;
	}

	@Override
	public Graphics fillRect(int x, int y, int width, int height) {
		surface.fillRectangle(x, y, width, height);
		return this;
	}

	@Override
	public Graphics shallow(int xoffset, int yoffset, int shaddowblur, int red,
			int green, int blue, double alpha) {
		surface.setShadowOffset(new Vector2(xoffset, yoffset));
		surface.setShadowColor(new Color(red, green, blue, alpha));
		surface.setShadowBlur(shaddowblur);
		return this;
	}

	@Override
	public Graphics setCompositeType(String type) {

		if (type.equals("source_over")) {
			surface.setGlobalCompositeOperation(Composition.SOURCE_OVER);
		} else if (type.equals("source_in")) {
			surface.setGlobalCompositeOperation(Composition.SOURCE_IN);
		} else if (type.equals("source_atop")) {
			surface.setGlobalCompositeOperation(Composition.SOURCE_ATOP);
		} else if (type.equals("dest_out")) {
			surface.setGlobalCompositeOperation(Composition.DESTINATION_OUT);
		} else if (type.equals("dest_over")) {
			surface.setGlobalCompositeOperation(Composition.DESTINATION_OVER);
		} else if (type.equals("lighter")) {
			surface.setGlobalCompositeOperation(Composition.LIGHTER);
		} else if (type.equals("xor")) {
			surface.setGlobalCompositeOperation(Composition.XOR);
		} else if (type.equals("copy")) {
			surface.setGlobalCompositeOperation(Composition.COPY);
		} else if (type.equals("dest_atop")) {
			surface.setGlobalCompositeOperation(Composition.DESTINATION_ATOP);
		} else if (type.equals("dest_in")) {
			surface.setGlobalCompositeOperation(Composition.DESTINATION_IN);
		} else if (type.equals("source_out")) {
			surface.setGlobalCompositeOperation(Composition.SOURCE_OUT);
		}
		return this;
	}

	@Override
	public Graphics drawQuadraticChart(double cx, double cy, double x,
			double y, double xt, double yt) {
		canvas.drawQuadraticCurve(x, y, cx, cy, xt, yt);
		return this;
	}

	@Override
	public Graphics drawBezierChart(double x, double y, double cx1, double cy1,
			double cx2, double cy2, double tx, double ty) {
		canvas.drawBezierCurve(x, y, cx1, cy1, cx2, cy2, tx, ty);
		return this;
	}

	@Override
	public Graphics drawBezierChartTo(double cx1, double cy1, double cx2,
			double cy2, double tx, double ty) {
		canvas.drawBezierCurveTo(cx1, cy1, cx2, cy2, tx, ty);
		return this;
	}

}
